﻿#include <conduit.hpp>
#include <conduit_relay.h>
#include <conduit_relay.hpp>
#include <iostream>
#include <string>
#include <type_traits>

using namespace std;
using namespace conduit;
using namespace relay;

void my_info_handler(const std::string& msg, const std::string& file, int line)
{
  std::cout << "[INFO]: " << msg << std::endl;
}
void my_warning_handler(const std::string& msg, const std::string& file, int line)
{
  std::cout << "[WARNING!]: " << msg << std::endl;
}
void my_error_handler(const std::string& msg, const std::string& file, int line)
{
  std::cout << "[ERROR!]: " << msg << std::endl;
  // errors are considered fatal, aborting or unwinding the
  // call stack with an exception are the only viable options
  throw conduit::Error(msg, file, line);
}

class Configuration
{
public:
  Configuration()
  {
    conduit::utils::set_info_handler(my_info_handler);
    conduit::utils::set_warning_handler(my_warning_handler);
    conduit::utils::set_error_handler(my_error_handler);
  };
  virtual ~Configuration(){};

  template <typename T>
  void setNodeValue(const string& name, T value)
  {
    try
    {
      dereference(name, value);
    }
    catch (conduit::Error& e)
    {
    }
  }
  /// <summary>
  /// 对char* 特殊处理
  /// </summary>
  /// <param name="name"></param>
  /// <param name="value"></param>
  void setNodeValue(const string& name, const char* value) { _node[name] = value; }

  /// <summary>
  /// 添加yaml到node中
  /// </summary>
  /// <typeparam name="T"></typeparam>
  /// <param name="name">key</param>
  /// <param name="value">yaml格式的字符串</param>
  /// <param name="valueType">yaml/json</param>
  void setNodeValue(const string& name, const string& value, const string& valueType)
  {
    try
    {
      using namespace conduit;
      if (valueType == "yaml")
      {
        Generator g(value, "yaml");
        Node n(g, true);
        _node[name] = n;
      }
      else if (valueType == "json")
      {
        Generator g(value, "json");
        Node n(g, true);
        _node[name] = n;
      }
    }
    catch (conduit::Error& e)
    {
    }
  }

  template <class T, typename = typename std::enable_if<std::is_pointer<T>::value>::type>
  T getNodeValue(const string& nodeName)
  {
    try
    {
      reinterpret_cast<T>(_node[nodeName].as_float32_ptr());
    }
    catch (conduit::Error &e)
    {
    }
    return reinterpret_cast<T>(_node[nodeName].as_float32_ptr());
  }

  template <class T, typename = typename std::enable_if<!std::is_pointer<T>::value>::type>
  typename std::enable_if<!std::is_pointer<T>::value, T>::type getNodeValue(const string& nodeName)
  {
   
     return getNodeValueNonPtr<T>(nodeName);
    
  }
  /// <summary>
  /// 获取Node中的数组，node数组元素默认类型，long long<==> int64；double <=> float64
  /// </summary>
  /// <typeparam name="vector"></typeparam>
  /// <param name="nodeName"></param>
  /// <returns></returns>
  template <typename T>
  vector<T> getNodeValueArray(const string& nodeName)
  {
    vector<T> vt;
    try
    {
      int count = _node[nodeName].dtype().number_of_elements();

      DataArray<T> dr = _node[nodeName].value();

      for (int i = 0; i < count; i++)
      {
        vt.push_back(dr[i]);
      }
    }
    catch (conduit::Error &e)
    {
    }
    return vt;
  }

  /// <summary>
  /// 打印node
  /// </summary>
  void printNode() { _node.print(); }
  /// <summary>
  /// 读取yml文件
  /// </summary>
  /// <param name="nodeName"></param>
  /// <param name="ymlFilePath"></param>
  void readYamlFile(string nodeName, string ymlFilePath)
  {
    try
    {
      conduit::Node n1;
      conduit::relay::io::load(ymlFilePath, n1);
      _node[nodeName] = n1;
    }
    catch (conduit::Error &e)
    {
    }
  }
  /// <summary>
  /// 将node保存为yaml文件
  /// </summary>
  /// <param name="ymlFilePath"></param>
  void saveYamlFile(string ymlFilePath) { conduit::relay::io::save(_node, ymlFilePath); }

  void clearNode()
  {
    CONDUIT_INFO("Clear Node Data");
    _node.reset();
  }

private:
  conduit::Node _node;

#pragma region setNodeValue
  ///解除引用机制 区分模板是 指针还是非指针
  template <typename T>
  typename std::enable_if<std::is_pointer<T>::value, typename std::remove_pointer<T>::type&>::type dereference(
    string name, T& t)
  {
    setNodeValuePtr(name, t);
    return *t;
  }
  template <typename T>
  typename std::enable_if<!std::is_pointer<T>::value, T&>::type dereference(string name, T& t)
  {
    setNodeValueNonPtr(name, t);
    return t;
  }

  /// <summary>
  /// 添加value到node中
  /// </summary>
  /// <typeparam name="T">value类型</typeparam>
  /// <param name="name">节点名</param>
  /// <param name="value">节点值</param>
  template <typename T>
  void setNodeValueNonPtr(const string& name, const T& value)
  {
    _node[name] = value;
  }

  /// <summary>
  /// 添加指针到节点中
  /// </summary>
  /// <param name="name"></param>
  /// <param name="vPtr"></param>
  void setNodeValuePtr(const string& name, void* vPtr)
  {
    _node[name].set_external_float32_ptr(reinterpret_cast<float*>(vPtr));
  }
#pragma endregion

  template <typename T>
  T getNodeValueNonPtr(const string& nodeName);

  string change(char* filePathbuf)
  {
    // filePathbuf变量是保存着相对路径的char型数组,dir用来保存绝对路径
    char dir[1024] = "";
// 1.转换绝对路径到dir
#ifdef _WIN32
    _fullpath(dir, filePathbuf, 1024);
#else
    realpath(filePathbuf, dir);
#endif
    // 2.替换绝对路径中的'/'为'\'
    //因为上述方法转换出来的绝对路径之间会以'/'分隔，例如“C:/user/desktop”,字符串处理遇到'/'一般会报错，下面一行代码将'/'全部替换为'\'
    for (int i = 0; dir[i] != 0 && i < 1024; i++)
    {
      if (dir[i] == 92)
      {
        dir[i] = '/';
      }
    }
    return dir;
  }
};

 template <>
string Configuration::getNodeValueNonPtr<string>(const string& nodeName)
{
   string result;
   try
   {
     result = _node[nodeName].to_string();
     //直接to_string后，字符串会带有多余的\" 做删除处理
     result.erase(std::remove(result.begin(), result.end(), '\"'), result.end());
   }
   catch (const std::exception&)
   {
   }
  return result;
}

template <>
int Configuration::getNodeValueNonPtr<int>(const string& nodeName)
{
  int result;
  try
  {
     _node[nodeName].to_int();
  }
  catch (const std::exception&)
  {
  }

  return result;
  
}

template <>
char Configuration::getNodeValueNonPtr<char>(const string& nodeName)
{
  char result;
  try
  {
    result = _node[nodeName].to_char();
  }
  catch (const std::exception&)
  {
  }
  return result;
}

template <>
double Configuration::getNodeValueNonPtr<double>(const string& nodeName)
{
  double result;
  try
  {
    result = _node[nodeName].to_double();
  }
  catch (const std::exception&)
  {
  }
  return result;
}

template <>
float Configuration::getNodeValueNonPtr<float>(const string& nodeName)
{
  float result;
  try
  {
    result = _node[nodeName].to_float();
    
  }
  catch (const std::exception&)
  {
  }
  return result;
}
